using System;
using System.Collections.Generic;
using System.Linq;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Framework.Serialization;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.Logic;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay
{
    public static class SkillFactory
    {
        private static Dictionary<int, SkillConfig> skillConfig = new Dictionary<int, SkillConfig>();
        private static Dictionary<string, SkillConfig> configCache = new Dictionary<string, SkillConfig>();
        private static Dictionary<string, SkillEffective_Package_ConfigData> prefabCache = new Dictionary<string, SkillEffective_Package_ConfigData>();

        // 创建预设的时候赋的Runtime时的GUID，用来区分同一预设在runtime时出现多份的情况，根据基础ID生成规则，此ID需要至少8位以上的数据
        private static long prefabGUID = 0x800;

        public static Func<string, ByteBuf> load { get; private set; }

        public static void SetLoadFunc(Func<string, ByteBuf> func)
        {
            load = func;
        }

        private static SkillEffective[] skillEffectives;

        public static void InitializeConfig()
        {
#region 技能效果算法缓存

            skillEffectives = new SkillEffective[(int)ESkillEffectiveType.Max];
            skillEffectives[(int)ESkillEffectiveType.Damage] = new SkillEffective_Damage();
            skillEffectives[(int)ESkillEffectiveType.Buff] = new SkillEffective_Buff();
            skillEffectives[(int)ESkillEffectiveType.InsertSkill] = new SkillEffective_InsertSkill();
            skillEffectives[(int)ESkillEffectiveType.HpToShield] = new SkillEffective_HpToShield();
            skillEffectives[(int)ESkillEffectiveType.Cure] = new SkillEffective_Cure();
            skillEffectives[(int)ESkillEffectiveType.CureToShield] = new SkillEffective_CureToShield();
            skillEffectives[(int)ESkillEffectiveType.ExtraDamage] = new SkillEffective_ExtraDamage();
            skillEffectives[(int)ESkillEffectiveType.CustomPropertyInt] = new SkillEffective_CustomPropertyInt();
            skillEffectives[(int)ESkillEffectiveType.Relive] = new SkillEffective_Relive();
            skillEffectives[(int)ESkillEffectiveType.ReplaceBuffMaker] = new Skilleffective_ReplaceBuffMaker();
            skillEffectives[(int)ESkillEffectiveType.Package] = new SkillEffective_Package();
            skillEffectives[(int)ESkillEffectiveType.ExtendBuffDuration] = new SkillEffective_ExtendBuffDuration();
            skillEffectives[(int)ESkillEffectiveType.ReplaceBuffDotType] = new SkillEffective_ReplaceBuffDotType();
            skillEffectives[(int)ESkillEffectiveType.ExtraTurn] = new Skilleffective_ExtraTurn();
            skillEffectives[(int)ESkillEffectiveType.DamageTempModify] = new SkillEffective_DamageTempModify();
            skillEffectives[(int)ESkillEffectiveType.ReplaceTargetSelect] = new SkillEffective_ReplaceTargetSelect();
            skillEffectives[(int)ESkillEffectiveType.ReplaceSkillType] = new SkillEffective_ReplaceSkillType();
            skillEffectives[(int)ESkillEffectiveType.WeaknessHit] = new SkillEffective_WeaknessHit();
            skillEffectives[(int)ESkillEffectiveType.Summon] = new SkillEffective_Summon();
            skillEffectives[(int)ESkillEffectiveType.Prefab] = new SkillEffective_Prefab();
            skillEffectives[(int)ESkillEffectiveType.Charge] = new SkillEffective_Charge();
            skillEffectives[(int)ESkillEffectiveType.Impact] = new SkillEffective_Impact();
            skillEffectives[(int)ESkillEffectiveType.Totem] = new SkillEffective_Totem();
            skillEffectives[(int)ESkillEffectiveType.PackageActionCondition] = new SkillEffective_PackageActionCondition();

#endregion
        }

        public static SkillConfig GetConfig(int nSkillID)
        {
            if (skillConfig.TryGetValue(nSkillID, out var rConfig))
            {
                return rConfig;
            }
            var rTabConfig = TableCenter.skill.Get(nSkillID);
            if (rTabConfig == null)
            {
                return null;
            }
            var rPath = PathConstant.GetSkillConfigPath(rTabConfig.ResFloder, rTabConfig.ResName);
            if (configCache.TryGetValue(rPath, out rConfig))
            {
                return rConfig;
            }

            var rConfigBuf = SkillFactory.load(rPath);
            if (rConfigBuf != null && rConfigBuf.Bytes.Length > 0)
            {
                rConfig = new SkillConfig();
                rConfig.DeSerialize(rConfigBuf);
                configCache.TryAdd(rPath, rConfig);
                skillConfig.TryAdd(nSkillID, rConfig);
                return rConfig;
            }

            return null;
        }

        public static Skill CreateSkill(int nSkillID, int lv, Unit rExecuter)
        {
            return CreateSkill(nSkillID, GetConfig(nSkillID), lv, rExecuter);
        }

        public static Skill CreateSkill(int nSkillID, SkillConfig rConfig, int lv, Unit rExecuter)
        {
            var rTabCfg = TableCenter.skill.Get(nSkillID);
            var rSkill = new Skill()
            {
                skillID = nSkillID,
                parts = new SkillPart[rConfig.parts.Count],
                triggers = new SkillTrigger[rConfig.triggers.Count],
                executer = rExecuter,
                config = rTabCfg,
                skillType = (ESkillType)(1 << rTabCfg.SkillType),
                cost = rTabCfg.SPCost,
                aiTargetSort = rTabCfg.AISelectType,
                lv = lv,
                initCd = rTabCfg.InitialCoolDown,
                norCd = rTabCfg.CoolDown,
            };
            rSkill.curCd = rSkill.initCd;
            // skillPart
            for (int i = 0; i < rConfig.parts.Count; i++)
            {
                var rPart = CreateSkillPart(rSkill, rConfig.parts[i]);
                rSkill.AddPart(i, rPart);
            }

            // Trigger
            for (int i = 0; i < rConfig.triggers.Count; i++)
            {
                var rTrigger = CreateSkillTrigger(rSkill, rConfig.triggers[i]);
                rSkill.triggers[i] = rTrigger;
            }

            for (int i = 0; i < rSkill.parts.Length; i++)
            {
                var rPart = rSkill.parts[i];
                var rPartConfig = rConfig.parts[i];
                if (rPart.partType == ESkillPartType.Main)
                {
                    rSkill.mainPart = rPart;
                }
                else if (rPart.partType == ESkillPartType.Enable)
                {
                    rSkill.enablePart = rPart;
                }
                else if (rPart.partType == ESkillPartType.Disable)
                {
                    rSkill.disablePart = rPart;
                }

                if (rPartConfig.nextGUID != 0)
                {
                    rPart.next = rSkill.parts.FirstOrDefault((x) => x.guid == rPartConfig.nextGUID);
                }
                else if (rPartConfig.condition != null)
                {
                    var rCondConfig = rPartConfig.condition;
                    var rCond = ClassPool.Get<SkillCondition>();
                    rCond.Initialize(rSkill, rCondConfig);
                    rCond.CheckFormula = rPartConfig.condition.formulaID;
                    rPart.condition = rCond;
                }
            }

            return rSkill;
        }

        private static SkillPart CreateSkillPart(Skill rParent, SkillPartConfig rConfig)
        {
            var rPart = ClassPool.Get<SkillPart>();
            rPart.skill = rParent;
            rPart.name = rConfig.name;
            rPart.partType = (ESkillPartType)rConfig.partType;
            rPart.timeline = rConfig.timeline;
            rPart.effectives = ListPool<SkillEffectiveData>.Get();
            rPart.guid = rConfig.guid;
            rPart.aiMoveType = rConfig.aiMoveType;
            for (int i = 0; i < rConfig.effectives.Count; i++)
            {
                var rItem = rConfig.effectives[i];
                var rType = (ESkillEffectiveType)rItem.type;

                var rEffectData = new SkillEffectiveData
                {
                    effective = GetEffective(rType),
                    skill = rParent,
                    select = TargetSelectFactory.CreateTargetSelectBySkill(rParent, rItem.target),
                    effectiveConfig = rItem,
                };
                rEffectData.effective.OnInitialize(rEffectData);

                rPart.effectives.Add(rEffectData);
            }

            return rPart;
        }

        /// <summary>
        /// 反序列化技能效果配置
        /// </summary>
        /// <param name="_buf"></param>
        /// <returns></returns>
        public static SkillCustomConfig CreateEffectiveConfig(int nType, ByteBuf _buf)
        {
            SkillCustomConfig rConfig = null;
            switch ((ESkillEffectiveType)nType)
            {
                case ESkillEffectiveType.Damage:
                    rConfig = new SkillEffective_Damage_ConfigData();
                    break;
                case ESkillEffectiveType.Buff:
                    rConfig = new SkillEffective_Buff_ConfigData();
                    break;
                case ESkillEffectiveType.InsertSkill:
                    rConfig = new SkillEffective_InsertSkill_ConfigData();
                    break;
                case ESkillEffectiveType.HpToShield:
                    rConfig = new SkillEffective_HpToShield_ConfigData();
                    break;
                case ESkillEffectiveType.Cure:
                    rConfig = new SkillEffective_Cure_ConfigData();
                    break;
                case ESkillEffectiveType.CureToShield:
                    rConfig = new SkillEffective_CureToShield_ConfigData();
                    break;
                case ESkillEffectiveType.ExtraDamage:
                    rConfig = new SkillEffective_ExtraDamage_ConfigData();
                    break;
                case ESkillEffectiveType.CustomPropertyInt:
                    rConfig = new SkillEffective_CustomProperty_ConfigData();
                    break;
                case ESkillEffectiveType.Relive:
                    rConfig = new SkillEffective_Relive_ConfigData();
                    break;
                case ESkillEffectiveType.Package:
                    rConfig = new SkillEffective_Package_ConfigData();
                    break;
                case ESkillEffectiveType.ExtendBuffDuration:
                    rConfig = new SkillEffective_ExtendBuffDuration_ConfigData();
                    break;
                case ESkillEffectiveType.ReplaceBuffDotType:
                    rConfig = new SkillEffective_RepalceBuffDotType_ConfigData();
                    break;
                case ESkillEffectiveType.ExtraTurn:
                    rConfig = new SkillEffective_ExtraTurn_ConfigData();
                    break;
                case ESkillEffectiveType.DamageTempModify:
                    rConfig = new SkillEffective_DamageTempModify_ConfigData();
                    break;
                case ESkillEffectiveType.ReplaceTargetSelect:
                    rConfig = new SkillEffective_ReplaceTargetSelect_ConfigData();
                    break;
                case ESkillEffectiveType.ReplaceSkillType:
                    rConfig = new SkillEffective_RepalceSkillType_ConfigData();
                    break;
                case ESkillEffectiveType.Summon:
                    rConfig = new SkillEffectiveAction_Summon_ConfigData();
                    break;
                case ESkillEffectiveType.Prefab:
                    rConfig = new SkillEffective_Prefab_ConfigData();
                    break;
                case ESkillEffectiveType.Charge:
                    rConfig = new SkillEffective_Charge_ConfigData();
                    break;
                case ESkillEffectiveType.Impact:
                    rConfig = new SkillEffective_Impact_ConfigData();
                    break;
                case ESkillEffectiveType.Totem:
                    rConfig = new SkillEffective_Totem_ConfigData();
                    break;
                default:
                    rConfig = new SkillCustomConfig();
                    break;
            }

            SerializeByteBufUtil.DeSerializeObject(_buf, ref rConfig);
            if (rConfig is SkillEffective_Prefab_ConfigData rPrefab)
            {
#if UNITY_EDITOR
                if (!Application.isPlaying)
                {
                }
                else
#endif
                {
                    SkillFactory.GetPrefabConfig(ref rPrefab);
                }
            }
            return rConfig;
        }

        /// <summary>
        /// 反序列化技能效果配置
        /// </summary>
        /// <param name="_buf"></param>
        /// <returns></returns>
        public static SkillCustomConfig CreateActionConfig(int nType, ByteBuf _buf)
        {
            SkillCustomConfig rConfig = null;
            switch ((ESkillEffectiveType)nType)
            {
                case ESkillEffectiveType.Damage:
                    rConfig = new SkillEffectiveAction_Damage_ConfigData();
                    break;
                case ESkillEffectiveType.Buff:
                    rConfig = new SkillEffectiveAction_Buff_ConfigData();
                    break;
                case ESkillEffectiveType.Cure:
                    rConfig = new SkillEffectiveAction_Cure_ConfigData();
                    break;
                case ESkillEffectiveType.CustomPropertyInt:
                    rConfig = new SkillEffectiveAction_CustomProperty_ConfigData();
                    break;
                case ESkillEffectiveType.ExtendBuffDuration:
                    rConfig = new SkillEffectiveAction_ExtendBuffDuration_ConfigData();
                    break;
                case ESkillEffectiveType.ReplaceBuffDotType:
                    rConfig = new SkillEffectiveAction_RepalceBuffDotType_ConfigData();
                    break;
                case ESkillEffectiveType.DamageTempModify:
                    rConfig = new SkillEffectiveAction_DamageTempModify_ConfigData();
                    break;
                case ESkillEffectiveType.Summon:
                    rConfig = new SkillEffectiveAction_Summon_ConfigData();
                    break;
                case ESkillEffectiveType.Charge:
                    rConfig = new SkillEffective_Charge_ConfigData();
                    break;
                case ESkillEffectiveType.Totem:
                    rConfig = new SkillEffective_Totem_ConfigData();
                    break;
                case ESkillEffectiveType.PackageActionCondition:
                    rConfig = new SkillEffectiveAction_PackageActionCondition_ConfigData();
                    break;
                default:
                    rConfig = new SkillCustomConfig();
                    break;
            }

            SerializeByteBufUtil.DeSerializeObject(_buf, ref rConfig);
            return rConfig;
        }

        /// <summary>
        /// 反序列化技能触发器配置
        /// </summary>
        /// <returns></returns>
        public static SkillCustomConfig CreateTriggerConfig(int nType, ByteBuf _buf)
        {
            SkillCustomConfig rConfig = null;
            switch ((ESkillTriggerType)nType)
            {
                case ESkillTriggerType.SelfAttacked:
                    rConfig = new SkillTrigger_SelfAttacked_ConfigData();
                    break;
                case ESkillTriggerType.SelfAttackOther:
                    rConfig = new SkillTrigger_SelfAttackOther_ConfigData();
                    break;
                case ESkillTriggerType.SelfAddBuff:
                    rConfig = new SkillTrigger_SelfAddBuff_ConfigData();
                    break;
                case ESkillTriggerType.SelfCustomProperty:
                    rConfig = new SkillTrigger_SelfCustomProperty_ConfigData();
                    break;
                case ESkillTriggerType.AddBuff:
                    rConfig = new SkillTrigger_AddBuff_ConfigData();
                    break;
                case ESkillTriggerType.ExecuteSkill:
                    rConfig = new SkillTrigger_ExecuteSkill_ConfigData();
                    break;
                case ESkillTriggerType.SkillDamageTempModify:
                    rConfig = new SkillTrigger_SkillDamageTempModify_ConfigData();
                    break;
                case ESkillTriggerType.Attacked:
                    rConfig = new SkillTrigger_Attacked_ConfigData();
                    break;
                case ESkillTriggerType.CustomProperty:
                    rConfig = new SkillTrigger_CustomProperty_ConfigData();
                    break;
                default:
                    break;
            }
            SerializeByteBufUtil.DeSerializeObject(_buf, ref rConfig);

            return rConfig;
        }

        private static void GetPrefabConfig(ref SkillEffective_Prefab_ConfigData rPrefab)
        {
            if (prefabCache.Count <= 0)
            {
                var rConfigBuf = SkillFactory.load?.Invoke(PathConstant.GetSkillPrefabPath());
                var rList = rConfigBuf.DeSerializeList<SkillEffective_PrefabSource_ConfigData>();
                for (int i = 0; i < rList.Count; i++)
                {
                    prefabCache.TryAdd(rList[i].key, rList[i].prefab);
                }
            }

            prefabCache.TryGetValue(rPrefab.prefab, out var rConfig);
            rPrefab.package = rConfig;
            rPrefab.guid = prefabGUID++;
        }

        /// <summary>
        /// 创建Trigger
        /// </summary>
        /// <param name="type"></param>
        /// <param name="rPart"></param>
        /// <returns></returns>
        private static SkillTrigger CreateSkillTrigger(Skill rSkill, TriggerConfig rConfig)
        {
            ESkillTriggerType type = (ESkillTriggerType)rConfig.type;
            SkillTrigger rTrigger;
            switch (type)
            {
                case ESkillTriggerType.SelfAttacked:
                    rTrigger = ClassPool.Get<SkillTrigger_SelfAttacked>();
                    break;
                case ESkillTriggerType.SelfAttackOther:
                    rTrigger = ClassPool.Get<SkillTrigger_SelfAttackOther>();
                    break;
                case ESkillTriggerType.SelfBattleStart:
                    rTrigger = ClassPool.Get<SkillTrigger_SelfStartBattle>();
                    break;
                case ESkillTriggerType.SelfAddBuff:
                    rTrigger = ClassPool.Get<SkillTrigger_SelfAddBuff>();
                    break;
                case ESkillTriggerType.SelfCustomProperty:
                    rTrigger = ClassPool.Get<SkillTrigger_SelfCustomProperty>();
                    break;
                case ESkillTriggerType.AddBuff:
                    rTrigger = ClassPool.Get<SkillTrigger_AddBuff>();
                    break;
                case ESkillTriggerType.ExecuteSkill:
                    rTrigger = ClassPool.Get<SkillTrigger_ExecuteSkill>();
                    break;
                case ESkillTriggerType.SkillDamageTempModify:
                    rTrigger = ClassPool.Get<SkillTrigger_SkillDamageTempModify>();
                    break;
                case ESkillTriggerType.Attacked:
                    rTrigger = ClassPool.Get<SkillTrigger_Attacked>();
                    break;
                case ESkillTriggerType.RoundEnd:
                    rTrigger = ClassPool.Get<SkillTrigger_RoundEnd>();
                    break;
                case ESkillTriggerType.CustomProperty:
                    rTrigger = ClassPool.Get<SkillTrigger_CustomProperty>();
                    break;
                case ESkillTriggerType.StartBattle:
                    rTrigger = ClassPool.Get<SkillTrigger_StartBattle>();
                    break;
                case ESkillTriggerType.StartRound:
                    rTrigger = ClassPool.Get<SkillTrigger_StartRound>();
                    break;
                case ESkillTriggerType.UnitDie:
                    rTrigger = ClassPool.Get<SkillTrigger_UnitDie>();
                    break;
                default:
                    if (type != ESkillTriggerType.Max && type != ESkillTriggerType.Min)
                        throw new Exception($"未实现{type}的触发器初始化");
                    rTrigger = null;
                    break;
            }

            rTrigger?.Initialize(rSkill, rConfig);
            return rTrigger;
        }

        public static SkillEffective GetEffective(ESkillEffectiveType type)
        {
            if (skillEffectives == null || skillEffectives.Length <= 0)
            {
                InitializeConfig();
            }

            return skillEffectives[(int)type];
        }

        private static void AddPart(this Skill rSelf, int nIndex, SkillPart rPart)
        {
            rSelf.parts[nIndex] = rPart;
            rSelf.partsDict.TryAdd(rPart.guid, rPart);
        }

        public static void ClearConfigCache()
        {
            skillConfig.Clear();
            configCache.Clear();
        }
    }
}